/*-------------------------------------------------------------------------
 *
 * pseudotypes.c
 *	  系统伪类型的函数。
 *
 * 伪类型并不是真正的类型并且从未有任何操作，但
 * 我们确实需要提供输入和输出函数以满足
 * pg_type 中伪类型条目的链接。在大多数情况下，函数
 * 只会在调用时抛出错误。 （XXX 这里的错误消息涵盖
 * 最常见的情况，但在某些上下文中可能会令人困惑。
 * 我们能做得更好吗？）
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/adt/pseudotypes.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "libpq/pqformat.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/rangetypes.h"
#include "utils/multirangetypes.h"


/*
 * 这些宏生成输入和输出函数，针对一个伪类型，
 * 将拒绝所有的输入和输出尝试。（但对于某些类型，只需
 * 输入函数是虚假的。）
 */
#define PSEUDOTYPE_DUMMY_INPUT_FUNC(typname) \
Datum \
typname##_in(PG_FUNCTION_ARGS) \
{ \
	ereport(ERROR, \
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
			 errmsg("cannot accept a value of type %s", #typname))); \
\
	PG_RETURN_VOID();			/* 保持编译器安静 */ \
} \
\
extern int no_such_variable

#define PSEUDOTYPE_DUMMY_IO_FUNCS(typname) \
PSEUDOTYPE_DUMMY_INPUT_FUNC(typname); \
\
Datum \
typname##_out(PG_FUNCTION_ARGS) \
{ \
	ereport(ERROR, \
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
			 errmsg("cannot display a value of type %s", #typname))); \
\
	PG_RETURN_VOID();			/* 保持编译器安静 */ \
} \
\
extern int no_such_variable

/*
 * 对于二进制发送/接收函数也是如此。我们对许多伪类型根本不
 * 费心，但有些类型有。（根据惯例，如果类型有发送函数，
 * 它应该有接收函数，即使那只是虚假的。）
 */
#define PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname) \
Datum \
typname##_recv(PG_FUNCTION_ARGS) \
{ \
	ereport(ERROR, \
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
			 errmsg("cannot accept a value of type %s", #typname))); \
\
	PG_RETURN_VOID();			/* 保持编译器安静 */ \
} \
\
extern int no_such_variable

#define PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(typname) \
PSEUDOTYPE_DUMMY_RECEIVE_FUNC(typname); \
\
Datum \
typname##_send(PG_FUNCTION_ARGS) \
{ \
	ereport(ERROR, \
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), \
			 errmsg("cannot display a value of type %s", #typname))); \
\
	PG_RETURN_VOID();			/* 保持编译器安静 */ \
} \
\
extern int no_such_variable


/*
 * cstring
 *
 * cstring 被标记为伪类型，因为我们不希望人们在
 * 表中使用它。但它实际上是一个功能完全的类型，因此提供
 * 一整套可用的 I/O 函数。除此之外，这
 * 允许手动调用数据类型的 I/O 函数，例如
 * "SELECT foo_in('blah')" 或 "SELECT foo_out(some-foo-value)"。
 */
Datum cstring_in(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

	PG_RETURN_CSTRING(pstrdup(fc_str));
}

Datum cstring_out(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);

	PG_RETURN_CSTRING(pstrdup(fc_str));
}

Datum cstring_recv(PG_FUNCTION_ARGS)
{
	StringInfo	fc_buf = (StringInfo) PG_GETARG_POINTER(0);
	char	   *fc_str;
	int			fc_nbytes;

	fc_str = pq_getmsgtext(fc_buf, fc_buf->len - fc_buf->cursor, &fc_nbytes);
	PG_RETURN_CSTRING(fc_str);
}

Datum cstring_send(PG_FUNCTION_ARGS)
{
	char	   *fc_str = PG_GETARG_CSTRING(0);
	StringInfoData fc_buf;

	pq_begintypsend(&fc_buf);
	pq_sendtext(&fc_buf, fc_str, strlen(fc_str));
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 * anyarray
 *
 * 我们需要允许 anyarray 的输出，以便，例如，pg_statistic 列
 * 可以被打印。然而，输入必须被禁止。
 *
 * XXX anyarray_recv 实际上可以工作，因为传入的
 * 数组数据将包含元素类型 OID。然而它似乎不太可能
 * 具有足够的类型安全性。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anyarray);
PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anyarray);

Datum anyarray_out(PG_FUNCTION_ARGS)
{
	return array_out(fcinfo);
}

Datum anyarray_send(PG_FUNCTION_ARGS)
{
	return array_send(fcinfo);
}

/*
 * anycompatiblearray
 *
 * 既然我们允许 anyarray 的输出，那么也可以允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblearray);
PSEUDOTYPE_DUMMY_RECEIVE_FUNC(anycompatiblearray);

Datum anycompatiblearray_out(PG_FUNCTION_ARGS)
{
	return array_out(fcinfo);
}

Datum anycompatiblearray_send(PG_FUNCTION_ARGS)
{
	return array_send(fcinfo);
}

/*
 * anyenum
 *
 * 既然 enum_out 实际上可行，那么也可以允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anyenum);

Datum anyenum_out(PG_FUNCTION_ARGS)
{
	return enum_out(fcinfo);
}

/*
 * anyrange
 *
 * 既然 range_out 实际上可行，那么也可以允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anyrange);

Datum anyrange_out(PG_FUNCTION_ARGS)
{
	return range_out(fcinfo);
}

/*
 * anycompatiblerange
 *
 * 既然 range_out 实际上可行，那么也可以允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblerange);

Datum anycompatiblerange_out(PG_FUNCTION_ARGS)
{
	return range_out(fcinfo);
}

/*
 * anycompatiblemultirange
 *
 * 既然 multirange_out 实际上可行，那么也可以允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(anycompatiblemultirange);

Datum anycompatiblemultirange_out(PG_FUNCTION_ARGS)
{
	return multirange_out(fcinfo);
}

/*
 * anymultirange_in		- 伪类型 ANYMULTIRANGE 的输入例程。
 */
Datum anymultirange_in(PG_FUNCTION_ARGS)
{
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("cannot accept a value of type %s", "anymultirange")));

	PG_RETURN_VOID();			/* 保持编译器安静 */
}

/*
 * anymultirange_out		- 伪类型 ANYMULTIRANGE 的输出例程。
 *
 * 既然 multirange_out 实际上可行，那么也可以允许这。
 */
Datum anymultirange_out(PG_FUNCTION_ARGS)
{
	return multirange_out(fcinfo);
}

/*
 * void
 *
 * 我们支持 void_in，以便 PL 函数可以在没有任何
 * 特殊黑客的情况下返回 VOID。PL 认为返回的任何值
 * 将被忽略。反之，void_out 和 void_send
 * 是必要的，以使 "SELECT function_returning_void(...)" 工作。
 */
Datum void_in(PG_FUNCTION_ARGS)
{
	PG_RETURN_VOID();			/* 你期待的是什么不同的？ */
}

Datum void_out(PG_FUNCTION_ARGS)
{
	PG_RETURN_CSTRING(pstrdup(""));
}

Datum void_recv(PG_FUNCTION_ARGS)
{
	/*
	 * 请注意，由于我们不消耗任何字节，因此尝试发送任何非空字符串将导致“无效消息格式”错误。
	 */
	PG_RETURN_VOID();
}

Datum void_send(PG_FUNCTION_ARGS)
{
	StringInfoData fc_buf;

	/* 发送一个空字符串 */
	pq_begintypsend(&fc_buf);
	PG_RETURN_BYTEA_P(pq_endtypsend(&fc_buf));
}

/*
 * shell
 *
 * shell_in 和 shell_out 被输入到 pg_type 中作为“shell”类型
 * （那些尚未填充的）。它们应该是无法到达的，但我们
 * 设定它们以防某些代码路径尝试在没有
 * 检查 pg_type.typisdefined 的情况下进行 I/O。
 */
Datum shell_in(PG_FUNCTION_ARGS)
{
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("cannot accept a value of a shell type")));

	PG_RETURN_VOID();			/* 保持编译器安静 */
}

Datum shell_out(PG_FUNCTION_ARGS)
{
	ereport(ERROR,
			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
			 errmsg("cannot display a value of a shell type")));

	PG_RETURN_VOID();			/* 保持编译器安静 */
}


/*
 * pg_node_tree
 *
 * pg_node_tree 其实并不是真正的伪类型——它真实到可以作为表列——
 * 但目前没有自己的操作，并且不允许输入，因此它的 I/O 函数在这里看起来适合。
 *
 * 我们必须禁止输入 pg_node_tree 值，因为操作该类型的 SQL 函数
 * 对格式错误的输入并不安全。我们确实想允许输出。
 */
PSEUDOTYPE_DUMMY_INPUT_FUNC(pg_node_tree);
PSEUDOTYPE_DUMMY_RECEIVE_FUNC(pg_node_tree);

Datum pg_node_tree_out(PG_FUNCTION_ARGS)
{
	return textout(fcinfo);
}

Datum pg_node_tree_send(PG_FUNCTION_ARGS)
{
	return textsend(fcinfo);
}

/*
 * pg_ddl_command
 *
 * 像 pg_node_tree 一样，pg_ddl_command 其实并不是真正的伪类型；它的存在
 * 原因与那个是一样的。
 *
 * 我们没有直接输出这种类型的好方法，因此也对输入和输出进行了处理。
 */
PSEUDOTYPE_DUMMY_IO_FUNCS(pg_ddl_command);
PSEUDOTYPE_DUMMY_BINARY_IO_FUNCS(pg_ddl_command);


/*
 * 各种其他伪类型的虚拟 I/O 函数。
 */
PSEUDOTYPE_DUMMY_IO_FUNCS(any);
PSEUDOTYPE_DUMMY_IO_FUNCS(trigger);
PSEUDOTYPE_DUMMY_IO_FUNCS(event_trigger);
PSEUDOTYPE_DUMMY_IO_FUNCS(language_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(fdw_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(table_am_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(index_am_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(tsm_handler);
PSEUDOTYPE_DUMMY_IO_FUNCS(internal);
PSEUDOTYPE_DUMMY_IO_FUNCS(anyelement);
PSEUDOTYPE_DUMMY_IO_FUNCS(anynonarray);
PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatible);
PSEUDOTYPE_DUMMY_IO_FUNCS(anycompatiblenonarray);
